#include "userhandler.h"
#include "sessionmanager.h"
#include "session.h"
#include "sessionfactory.h"

#include "../account/account.h"
#include "../config/config.h"
#include "../utils/logger.h"
#include "../service/servicexml/servicexml.h"

#include <QString>
#include <QRegExp>
#include <QStringList>
#include <QFile>
#include <stdio.h>


UserCallback::UserCallback(UserHandler *handler)
:PjApiUaAccountCallback(handler->getPjApiHandler())
{
    m_userHandler = handler;
}

UserCallback::~UserCallback()
{
}

//call back
void UserCallback::onIncomingCall(int account_id,
                            int call_id,
                            void *data)
{
    if(account_id != m_userHandler->getPjApiHandler()->getAccount()->getAccountID())
    {
        LOG_DEBUG("Usercall back: ERROR: invalid account id\n");
        return;
    }

    if(call_id <= -1)
    {
        //Error handler
        LOG_DEBUG("eroor incoming call\n");
        return;
    }

    //Here we just for testing and accept all call auto
    m_userHandler->acceptSession(call_id);

    LOG_DEBUG("accept new seesion");
    SessionFactory *factory = new SessionFactory();
    Session *session = factory->createSession(call_id, this->getUserHandler());
    /** ??? To to confirmed if need add session for server role */
   // SessionManager::getInstance()->addSession(session);
   // session->start();

    delete factory;
    LOG_DEBUG("Load service config file");
    QFile xml_file(QString::fromStdString(Config::getInstance()->getServiceConfigFile()));
    ServiceXml *xml = new ServiceXml(xml_file);
    //xml->doProcess();
    session->sendServiceInfo(xml->getContentType(), xml->getLength(), xml->preSendProcess().c_str());
    LOG_DEBUG("Ready to send service infomation xml: \n %s", xml->preSendProcess().c_str());

}

void UserCallback::onInfoRequest(int session_id,
                            std::string request_name,  // This parameter will not be used
                            std::string content_type,
                            unsigned long long content_length,
                            void *data)
{
    if(content_type == "text/xml")
    {
        LOG_DEBUG("start process the received service infomation from xml to html");
        ServiceXml *xml = new ServiceXml(((std::string)((char*)data)));
        std::string html = xml->postReceivedProcess();
        SessionManager::getInstance()->
                        getSessionByID(session_id)->
                        getSessionCallback()->
                        serviceInfoReceived("text/html", html.length(), html.c_str());
    }
    else
    {
        LOG_DEBUG("Service info format is not correct");
    }



#if 0 // Notice: this section is too old, here we do not use it after refactoring but still keep it.
    QString name = QString::fromStdString(request_name);
    QRegExp rx(QString::fromStdString("*::*::*"));
    rx.setPatternSyntax(QRegExp::Wildcard);
    if (!rx.exactMatch(name))
    {
        // Error, reqeust name is not valid
        return;
    }

    QStringList name_list = name.split(QString::fromStdString("::"));

    std::string message_type = name_list[0].toStdString();
    std::string service_name = name_list[1].toStdString();
    std::string request_id = name_list[2].toStdString();

    std::string name_parsed = service_name + "::" + request_id;

    if(message_type == "Request")
    {
        SessionManager::getInstance()->
                        getSessionByID(session_id)->
                        getSessionCallback()->
                        serviceRequestReceived(name_parsed, content_type, content_length, data);
    }
    else if(message_type == "Response")
    {
        SessionManager::getInstance()->
                        getSessionByID(session_id)->
                        getSessionCallback()->
                        serviceResponseReceived(name_parsed, content_type, content_length, data);
    }
    else
    {
        //Error
        //LOG();
        return;
    }
#endif
}

void UserCallback::onInfoResponse(int session_id,
                            std::string response_name,
                            std::string content_type,
                            unsigned long long content_length,
                            void *data)
{
}

void UserCallback::onIMMessage(int call_id,
                        const std::string from,
                        const std::string to,
                        const std::string contact,
                        const std::string mime_type,
                        const std::string body)
{
}

void UserCallback::onIMTyping(int call_id,
                        std::string from,
                        std::string to,
                        std::string contact,
                        bool isTyping)
{
}


void UserCallback::onStatlessRequestReceived(std::string from,
                            std::string request_name,
                            std::string content_type,
                            unsigned long long content_length,
                            void *data)
{
    LOG_DEBUG("Stateless request received!++++++++\n from: %s\n request_name: %s\n data : %s \n",
                    from.c_str(), request_name.c_str(), (char*)data);

    QString name = QString::fromStdString(request_name);
    QRegExp rx(QString::fromStdString("*::*::*"));
    rx.setPatternSyntax(QRegExp::Wildcard);
    if (!rx.exactMatch(name))
    {
        // Error, reqeust name is not valid
        return;
    }

    QStringList name_list = name.split(QString::fromStdString("::"));

    std::string message_type = name_list[0].toStdString();
    std::string service_name = name_list[1].toStdString();
    std::string request_id = name_list[2].toStdString();

    std::string name_parsed = service_name + "::" + request_id;

    if(message_type == "Request")
    {
        SessionManager::getInstance()->
                        getSessionByFrom(from)->
                        getSessionCallback()->
                        serviceRequestReceived(name_parsed, content_type, content_length, data);
    }
    else if(message_type == "Response")
    {
        SessionManager::getInstance()->
                        getSessionByFrom(from)->
                        getSessionCallback()->
                        serviceResponseReceived(name_parsed, content_type, content_length, data);
    }
    else
    {
        //Error
        //LOG();
        return;
    }
}

UserHandler::UserHandler(Account *account)
{
    LOG_DEBUG("Init User Handler start");
    m_handler = new PjApiUaAccountHandler();
    m_account = account;
    LOG_DEBUG("Account ID : %s", account->getUsername().c_str());
    LOG_DEBUG("Account password : %s", account->getPassword().c_str());
    m_mutex = new QMutex(QMutex::Recursive);
    LOG_DEBUG("Init User Handler end");
}

UserHandler::~UserHandler()
{
    delete m_handler;
    delete m_mutex;
}

void UserHandler::init()
{

    initConfig();
    LOG_DEBUG("Init user handler config end");
    m_handler->start();
    LOG_DEBUG("handler start OK");
    initAccount();
    LOG_DEBUG("Init user handler account end");
    //LOG_DEBUG("Init user handler account end");
    initCallback();
    //LOG_DEBUG("Init user handler callback end");
}

void UserHandler::initAccount()
{
    m_handler->setAccount(m_account);
}

void UserHandler::initConfig()
{
    if( ! m_handler )
    {
        LOG_DEBUG("Init cofig failed since m_handler is null");
        return;
    }

    m_handler->setRtpPort(4000);
    LOG_DEBUG("Set RTP port end");
    m_handler->setTransportConfig();
    LOG_DEBUG("Set TransportConfig");
}

void UserHandler::initCallback()
{
    m_handler->setAccountCallback(new UserCallback(this));
}

PjApiUaAccountCallback* UserHandler::getUserCallback()
{
    return m_handler->getAccountCallback();
}

// start a session with to
int UserHandler::startSession(std::string to)
{
    QMutexLocker locker(m_mutex);

    // To do
    // Get service list add to content to notify the remote
    // Here we skip it
    int session_id = m_handler->makeCall(to, 0, "", "");

    if(session_id <= -1)
    {
        //Error handler
        LOG_DEBUG("eroor maek call");
        return -1;
    }

    LOG_DEBUG("new seesion");
    SessionFactory *factory = new SessionFactory();
    Session *session = factory->createSession(session_id, this);
    SessionManager::getInstance()->addSession(session);
    session->start();

    delete factory;
}

void UserHandler::acceptSession(int session_id)
{
    QMutexLocker locker(m_mutex);

    if(session_id <= -1)
    {
        //Error
        return;
    }
    // To do, need to get service list from Service module and notify the remote.
    if(m_handler->answerCall(session_id, 200, "", "", "") == -1)
    {
        //Error
        return;
    }

    SessionFactory *factory = new SessionFactory();
    Session *session = factory->createSession(session_id, this);
    SessionManager::getInstance()->addSession(session);
    session->start();

    delete factory;
}

//to reject an incoming session
void UserHandler::rejectSession(int session_id)
{
    QMutexLocker locker(m_mutex);
}

//to terminate an active session
void UserHandler::terminateSession(int session_id)
{
    QMutexLocker locker(m_mutex);

    if(m_handler->hangupCall(session_id, 603, "terminate", "", "") == -1)
    {
        //Error
        return;
    }

    SessionManager::getInstance()->removeSession(session_id);
}

void UserHandler::sendPackageInSession(int call_id,
                        const std::string name,
                        const std::string content_type,
                        const void* content)
{
    QMutexLocker locker(m_mutex);

    if(call_id <= -1)
    {
        //
        return;
    }

    if(m_handler->sendPackage(call_id, name, content_type, content) == -1)
    {
        //Error
        return;
    }
}

void UserHandler::sendIMOutSession(std::string to_uri, std::string mime_type, std::string content)
{
    QMutexLocker locker(m_mutex);

    if(!m_handler->verifyUrl(to_uri))
    {
        // Error: the remote URL is not valid
        return;
    }

    if(m_handler->sendIM(to_uri, mime_type, content) == -1)
    {
        //Error: send IM outsession falied
        return;
    }
}

void UserHandler::sendIMTyping(std::string to_uri, bool is_typing)
{
    QMutexLocker locker(m_mutex);

    if(!m_handler->verifyUrl(to_uri))
    {
        // Error: the remote URL is not valid
        return;
    }

    if(m_handler->sendIMTyping(to_uri, is_typing) == -1)
    {
        //Error: send IM outsession falied
        return;
    }
}

void UserHandler::sendPackageStateless(const std::string target_uri, const std::string from, const std::string to, const std::string contact,
                                const std::string pkg_name, const std::string content_type, const void* content)
{
    QMutexLocker locker(m_mutex);

    if( ! ( (m_handler->verifyUrl(target_uri)) &&
             (m_handler->verifyUrl(from)) &&
             (m_handler->verifyUrl(to)) &&
             (m_handler->verifyUrl(contact))))
    {
        // Error: the remote URL is not valid
        return;
    }

    if(m_handler->sendPackageStateless(target_uri, from, to, contact, pkg_name, content_type, content) == -1)
    {
        //Error: send IM outsession falied
        return;
    }
}
